이번주는 라이브러리를 다운로드 받아서 진행할경우 오류가 많이 발생해서
아두이노 자체에서 라이브러리를 추가하는 방식으로 진행 예정입니다.
이번주에 가속도와 회전까지 진행해 볼 예정입니다
I2Cdev_library.zip [ LINK ]
MPU6050_library.zip [ LINK ]
[YouTube] 049. 아두이노 모든 부속 - MPU6050(가속도 자이로 센서), kProject Arduino DIY [ LINK ]
[블러그] Dreaming Developer, 드론-MPU6050 가속도 자이로 센서 이해, 네이버 [ LINK ]
▪ MPU6050은 InvenSense에서 개발한 고성능 6축 관성 측정 장치이다.
MPU6050는 저전력, 고정확도, 적은 크기 및 비용 효율성 등의 장점을 가지고 있어서 드론, 자율 주행 차량, 가상 현실 헤드셋, 웨어러블 디바이스 및 로봇 제어 등 다양한 응용 분야에서 널리 사용되고 있습니다.
YPR은 자이로스코프와 가속도계를 사용하여 측정한 자세를 나타낸다.
YPR은 yaw (yaw rate), pitch (pitch angle), roll (roll angle)의 약어이다.
YPR은 자이로스코프와 가속도계를 통해 측정한 자세를 나타냅니다. 이는 운동을 감지하고 그에 따른 회전 속도와 기울기를 파악하는 데에 중요한 지표입니다.
이러한 YPR 값들은 다양한 응용 분야에서 사용됩니다.
예를 들어, 드론이나 자율 주행 차량에서는 안정적인 비행이나 주행을 유지하기 위해 사용될 뿐만 아니라, 항공기에서는 헤딩을 조절하며
가상 현실 헤드셋에서는 사용자의 시점을 추적하여 현실감 있는 경험을 제공합니다.
#include // MPU6050 센서 라이브러리를 불러옵니다.
#include // 센서 라이브러리를 불러옵니다.
#include // I2C 통신을 위한 라이브러리를 불러옵니다.
Adafruit_MPU6050 mpu; // MPU6050 객체를 생성합니다.
void setup(void) {
Serial.begin(115200); // 시리얼 통신을 115200 보드레이트로 시작합니다.
// MPU6050 센서 초기화
while (!mpu.begin()) { // MPU6050이 초기화되지 않았을 때 반복합니다.
Serial.println("MPU6050 not connected!"); // MPU6050이 연결되지 않았음을 알리는 메시지를 출력합니다.
delay(1000); // 1초의 딜레이를 줍니다.
}
Serial.println("MPU6050 ready!"); // MPU6050이 초기화되었음을 알리는 메시지를 출력합니다.
}
sensors_event_t event; // 센서 이벤트 객체를 생성합니다.
void loop() {
mpu.getAccelerometerSensor()->getEvent(&event); // 가속도 센서에서 이벤트를 가져옵니다.
// 시리얼로 가속도 데이터 출력
Serial.print(event.acceleration.x); // X축 가속도 값을 출력합니다.
Serial.print(","); // 쉼표로 구분합니다.
Serial.print(event.acceleration.y); // Y축 가속도 값을 출력합니다.
Serial.print(","); // 쉼표로 구분합니다.
Serial.println(event.acceleration.z); // Z축 가속도 값을 출력하고 개행합니다.
delay(100); // 100ms의 딜레이를 줍니다.
}
#include // Adafruit_MPU6050 라이브러리를 불러옵니다.
#include // Adafruit_Sensor 라이브러리를 불러옵니다.
#include // I2C 통신을 위한 Wire 라이브러리를 불러옵니다.
Adafruit_MPU6050 mpu; // Adafruit_MPU6050 객체를 생성합니다.
void setup(void) {
Serial.begin(115200); // 시리얼 통신을 115200 보드레이트로 시작합니다.
while (!mpu.begin()) { // MPU6050 초기화가 실패할 경우 반복합니다.
Serial.println("MPU6050 not connected!"); // MPU6050이 연결되지 않았음을 알리는 메시지를 출력합니다.
delay(1000); // 1초 대기합니다.
}
Serial.println("MPU6050 ready!"); // MPU6050 초기화가 완료되면 메시지를 출력합니다.
}
sensors_event_t event; // 센서 이벤트를 저장할 객체를 선언합니다.
void loop() {
mpu.getGyroSensor()->getEvent(&event); // 자이로 센서에서 이벤트를 읽어옵니다.
// 시리얼 모니터로 자이로 데이터를 출력합니다.
Serial.print(event.gyro.x); // X축 자이로 값 출력합니다.
Serial.print(","); // 쉼표로 구분합니다.
Serial.print(event.gyro.y); // Y축 자이로 값 출력합니다.
Serial.print(","); // 쉼표로 구분합니다.
Serial.println(event.gyro.z); // Z축 자이로 값 출력합니다.
delay(100); // 100ms의 딜레이를 줍니다.
}
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif
MPU6050 mpu; // MPU6050 객체를 생성합니다.
#define OUTPUT_READABLE_YAWPITCHROLL // Yaw, Pitch, Roll 값을 출력합니다.
#define INTERRUPT_PIN 2 // 인터럽트 핀을 2번으로 설정합니다.
bool dmpReady = false; // DMP 준비 상태를 나타내는 플래그 변수입니다.
uint8_t mpuIntStatus; // MPU6050의 인터럽트 상태를 저장하는 변수입니다.
uint8_t devStatus; // MPU6050의 장치 상태를 저장하는 변수입니다.
uint16_t packetSize; // DMP 패킷의 크기를 저장하는 변수입니다.
uint16_t fifoCount; // FIFO 버퍼의 현재 크기를 저장하는 변수입니다.
uint8_t fifoBuffer[64]; // FIFO 버퍼를 저장하는 배열입니다.
Quaternion q; // 쿼터니언을 저장하는 변수입니다.
VectorInt16 aa; // 가속도계의 가속도 값을 저장하는 변수입니다.
VectorInt16 aaReal; // 가속도계의 실제 가속도 값을 저장하는 변수입니다.
VectorInt16 aaWorld; // 가속도계의 월드 좌표계 가속도 값을 저장하는 변수입니다.
VectorFloat gravity; // 중력 벡터를 저장하는 변수입니다.
float euler[3]; // 오일러 각을 저장하는 변수입니다.
float ypr[3]; // yaw, pitch, roll 값을 저장하는 변수입니다.
uint8_t teapotPacket[14] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' }; // 티팟 패킷을 설정합니다.
volatile bool mpuInterrupt = false; // MPU6050 인터럽트 상태를 나타내는 플래그 변수입니다.
// MPU6050 인터럽트 핸들러 함수입니다.
void dmpDataReady() {
mpuInterrupt = true; // MPU6050 인터럽트 상태를 설정합니다.
}
// 설정 함수입니다.
void setup() {
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin(); // I2C 통신을 초기화합니다.
Wire.setClock(400000); // I2C 통신 속도를 설정합니다.
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true); // Fastwire를 설정합니다.
#endif
Serial.begin(115200); // 시리얼 통신을 초기화합니다.
while (!Serial); // 시리얼 통신이 준비될 때까지 대기합니다.
Serial.println(F("Initializing I2C devices...")); // I2C 장치 초기화 메시지를 출력합니다.
mpu.initialize(); // MPU6050을 초기화합니다.
pinMode(INTERRUPT_PIN, INPUT); // 인터럽트 핀을 입력으로 설정합니다.
Serial.println(F("Testing device connections...")); // 장치 연결 테스트 메시지를 출력합니다.
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed")); // MPU6050 연결 상태를 출력합니다.
Serial.println(F("\nSend any character to begin DMP programming and demo: ")); // DMP 프로그래밍 및 데모 시작 메시지를 출력합니다.
while (Serial.available() && Serial.read()); // 시리얼 버퍼를 비웁니다.
while (!Serial.available()); // 시리얼 입력이 있을 때까지 대기합니다.
while (Serial.available() && Serial.read()); // 시리얼 버퍼를 비웁니다.
Serial.println(F("Initializing DMP...")); // DMP 초기화 메시지를 출력합니다.
devStatus = mpu.dmpInitialize(); // DMP를 초기화합니다.
mpu.setXGyroOffset(220); // X축 자이로 오프셋을 설정합니다.
mpu.setYGyroOffset(76); // Y축 자이로 오프셋을 설정합니다.
mpu.setZGyroOffset(-85); // Z축 자이로 오프셋을 설정합니다.
mpu.setZAccelOffset(1788); // Z축 가속도 오프셋을 설정합니다.
if (devStatus == 0) { // 초기화 상태가 정상인 경우
Serial.println(F("Enabling DMP...")); // DMP 활성화 메시지를 출력합니다.
mpu.setDMPEnabled(true); // DMP를 활성화합니다.
Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)...")); // 인터럽트 감지 활성화 메시지를 출력합니다.
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING); // 인터럽트를 설정합니다.
mpuIntStatus = mpu.getIntStatus(); // MPU6050의 인터럽트 상태를 확인합니다.
Serial.println(F("DMP ready! Waiting for first interrupt...")); // DMP 준비 완료 메시지를 출력합니다.
dmpReady = true; // DMP 준비 상태를 설정합니다.
packetSize = mpu.dmpGetFIFOPacketSize(); // DMP 패킷 크기를 가져옵니다.
} else { // 초기화 상태가 비정상인 경우
Serial.print(F("DMP Initialization failed (code ")); // DMP 초기화 실패 메시지를 출력합니다.
Serial.print(devStatus); // 실패 코드를 출력합니다.
Serial.println(F(")")); // 줄바꿈 문자를 출력합니다.
}
}
// 루프 함수입니다.
void loop() {
if (!dmpReady) return; // DMP가 준비되지 않았으면 함수를 종료합니다.
while (!mpuInterrupt && fifoCount < packetSize) {} // MPU6050 인터럽트가 발생하거나 FIFO 버퍼 크기가 패킷 크기보다 작을 때까지 대기합니다.
mpuInterrupt = false; // MPU6050 인터럽트 상태를 초기화합니다.
mpuIntStatus = mpu.getIntStatus(); // MPU6050의 인터럽트 상태를 가져옵니다.
fifoCount = mpu.getFIFOCount(); // FIFO 버퍼 크기를 가져옵니다.
if ((mpuIntStatus & 0x10) || fifoCount == 1024) { // FIFO 오버플로우 상태인 경우
mpu.resetFIFO(); // FIFO를 재설정합니다.
Serial.println(F("FIFO overflow!")); // FIFO 오버플로우 메시지를 출력합니다.
} else if (mpuIntStatus & 0x02) { // DMP 데이터가 준비된 경우
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount(); // FIFO 버퍼 크기가 패킷 크기보다 작을 때까지 대기합니다.
mpu.getFIFOBytes(fifoBuffer, packetSize); // FIFO 버퍼에서 패킷 데이터를 읽어옵니다.
fifoCount -= packetSize; // FIFO 버퍼 크기를 조정합니다.
mpu.dmpGetQuaternion(&q, fifoBuffer); // 쿼터니언 데이터를 가져옵니다.
mpu.dmpGetGravity(&gravity, &q); // 중력 벡터를 가져옵니다.
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity); // Yaw, Pitch, Roll 값을 계산합니다.
Serial.print("ypr\t"); // YPR 값의 레이블을 출력합니다.
Serial.print(ypr[0] * 180/M_PI); // Yaw 값을 출력합니다.
Serial.print("\t"); // 탭 문자를 출력합니다.
Serial.print(ypr[1] * 180/M_PI); // Pitch 값을 출력합니다.
Serial.print("\t"); // 탭 문자를 출력합니다.
Serial.println(ypr[2] * 180/M_PI); // Roll 값을 출력하고 줄을 바꿉니다.
}
}